#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>

#include <sys/un.h>
#include <getopt.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#define TAM 2048


int main( int argc, char *argv[] ) {
	// Para TCP //
	int sockfd, newsockfd, puerto, clilen, pid;								// Definición de variables
	char buffer[TAM];														// Buffer del servidor
	struct sockaddr_in serv_addr, cli_addr;
	int n;																	// Entero de chequeo
	char cmd[TAM];															// Aquí voy a almacenar el comando
	char *directorio;														// Path para ejecutar el chdir
	char ok[] =  "cd OK: path correcto\n";									// Mensaje para path correcto
	char not[] = "cd error: path incorrecto\n";								// Mensaje para path incorrecto
	char cmderr[] = "comando incorrecto\n";									// Mensaje para comando incorrecto
	char put[] = "Comienza la transferencia...\n";
	// Para UDP //
	int socket_server, resultado;
	int descriptor_socket, cantidad, i;
	int x=0;
	struct sockaddr_un struct_servidor;
	struct sockaddr_un struct_cliente;
	
	char transf[256];
	char transf2[256];
	int m;
	char *ptr;
	unsigned long fsize=0;
	unsigned long grabado=0;													// Mantengo la cuenta de cuanto se grabo del archivo.
	FILE *fp;
	char *uport[10];
	int size=0;
	struct stat sts;
	uport[0]="6210";
	int puertoUDP=6210;
	//int descriptor_socketUDP;
	//struct sockaddr_un struct_UDP;
	//socklen_t tamano_direccion;
	
	if ( argc < 2 ) {
        	fprintf( stderr, "Uso: %s <puerto>\n", argv[0] );
		exit( 1 );
	}
///////////////////////////////////////////////////////////////////////////////
/*	
if(( descriptor_socketUDP = socket(AF_UNIX, SOCK_DGRAM, 0) ) < 0 ) {
		perror("socket" );
		exit(1);
	}
	
	// Remover el nombre de archivo si existe //
  	unlink ( uport[0] );
	// Inicialización y establecimiento de la estructura del servidor para udp //
	memset( &struct_UDP, 0, sizeof( struct_UDP ) );
	struct_UDP.sun_family = AF_UNIX;
	strncpy( struct_UDP.sun_path, uport[0], sizeof( struct_UDP.sun_path ) );
	// Ligadura del socket de servidor udp a una dirección //
	if( ( bind( descriptor_socketUDP, (struct sockaddr *)&struct_UDP, SUN_LEN(&struct_UDP ))) < 0 ) {
		perror( "bind" );
		exit(1);
	}
	tamano_direccion = sizeof( struct_UDP );*/
	
///////////////////////////////////////////////////////////////////////////////
 int descriptor_socketUDP = socket( AF_INET, SOCK_DGRAM, 0 );
        if (descriptor_socketUDP < 0) {perror("ERROR abriendo el Socket UDP");}
 struct sockaddr_in struct_UDP;
        memset((char *) &struct_UDP, 0, sizeof(struct_UDP) );
        struct_UDP.sin_family = AF_INET;
        struct_UDP.sin_addr.s_addr = INADDR_ANY;
        struct_UDP.sin_port = htons( puertoUDP );
        memset( &(struct_UDP.sin_zero), '\0', 8 );
        n=bind( descriptor_socketUDP, (struct sockaddr *) &struct_UDP, sizeof(struct_UDP) );
        if(n<0 ) {perror( "ERROR en ligadura UDP" );exit(0);}
        int tamano_direccion = sizeof( struct sockaddr );
///////////////////////////////////////////////////////////////////////////////
	// PUERTO Y SOCKET STREAM CON CONEXION //
	sockfd = socket( AF_INET, SOCK_STREAM, 0);								
	if ( sockfd < 0 ) { 
		perror( " apertura de socket ");
		exit( 1 );
	}

	memset( (char *) &serv_addr, 0, sizeof(serv_addr) );
	puerto = atoi( argv[1] );
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = INADDR_ANY;
	serv_addr.sin_port = htons( puerto );

	if ( bind(sockfd, ( struct sockaddr *) &serv_addr, sizeof( serv_addr ) ) < 0 ) {
		perror( "ligadura" );
		exit( 1 );
	}

        printf( "Proceso: %d - socket disponible: %d\n", getpid(), ntohs(serv_addr.sin_port) );

	listen( sockfd, 5 );
	clilen = sizeof( cli_addr );

	while( 1 ) {
		newsockfd = accept( sockfd, (struct sockaddr *) &cli_addr, &clilen ); 		// Cuando un cliente se conecta al puerto
		if ( newsockfd < 0 ) {														// se acepta la conexion y se obtiene un file descriptor para ese cliente
			perror( "accept" );
			exit( 1 );
		}

		pid = fork(); 																// Generamos un proceso hijo para tratar a dicho cliente
		if ( pid < 0 ) {
			perror( "fork" );
			exit( 1 );
		}

		if ( pid == 0 ) {  // Proceso hijo
			close( sockfd );														// En el hijo cerramos el file descriptor sockfd que es el del padre

			while ( 1 ) {
				memset( buffer, 0, TAM );											// Limpiamos tanto buffer como cmd
				memset( cmd,0, TAM );

				n = read( newsockfd, buffer, TAM );								// El server se queda leyendo del buffer
				if ( n < 0 ) {
					perror( "lectura de socket" );
					exit(1);
				}
									
				strcpy(cmd,buffer);													// Una vez que se produce la lectura del buffer vamos a analizar que ingreso 
				strtok(cmd," ");													// el cliente.
				strcpy(transf,buffer); 												// Almaceno en cmd el comando ejecutado.
				
/////////// CD O PUT //////////////////////////
				if(!strcmp("cd",cmd)||!strcmp("put",cmd)||!strcmp("get",cmd))										// Observo si ingresó cd
					{
					int ret=0;
					directorio= strstr(buffer," ");							// En directorio dejo el path
					directorio++;											// Acomodo directorio para que quede correctamente
																			// y pueda ejecutar luego el chdir
/////////////   CD   /////////////////////////// 
					if(!strcmp("cd",cmd))
						{
						directorio[strlen(directorio)-1] = '\0';
						ret= chdir(directorio);									// Hago chdir a ese path
						memset( cmd,0, TAM );									// Limpio cmd y buffer para poder imprimir luego el mensaje de cd
						memset( buffer,0, TAM );
						if(ret==0)
						{	int nuevofd=dup(1);							// Si el comando ingresado no fue ni cd ni fin entonces es cualquier otro comando
							dup2(newsockfd,1);
							int r=system("pwd");
							dup2(nuevofd,1);
							//strcpy(buffer,ok);
							}							// Chequeamos si el path a sido correcto
						else{strcpy(buffer,not);}								// Si no, quiere decir que el chdir no se a podido completar con ÉXITO
						} // Fin CD
////////////    PUT    ////////////////////////
						else
						{ 	
							if(!strcmp("put",cmd))
							{
							// Vamos a dejar el nombre del archivo solo, sin el path
							printf("directorio %s\n",directorio);
							printf("buffer %s\n",buffer);
							fp = fopen(directorio, "rb");
							fseek(fp,0,SEEK_END);
							fsize = ftell(fp); // Vemos el tamano del archivo
							rewind(fp);
							fclose(fp);
							
							if(strpbrk(directorio,"/")!=NULL)
							{ptr=strpbrk(directorio,"/");
							
							while(strpbrk(ptr,"/")!= NULL )
							{	ptr = strpbrk(ptr,"/");
								ptr=ptr+1;
								directorio=ptr;
							}
							}
							else{directorio[strlen(directorio)-1] = '\0';}
							printf("directorio %s\n",directorio);
							// Abrimos el archivo. Claramente lo va a crear porque no existe
							fp = fopen(directorio, "w+b");
							if (fp == NULL) 
								{printf("File not found!\n");}
							else 
								{
										printf("Archivo %s con %lu\n", directorio,fsize);
										// Respondemos al cliente que estamos transfiriendo
										memset(buffer,0,TAM);
										strcpy(buffer,put);
										n = write( newsockfd, buffer,strlen(buffer) );
										memset(buffer,0,TAM);
									
									 n = recvfrom( descriptor_socketUDP, buffer, TAM, 0, (struct sockaddr *)&struct_UDP,  (socklen_t*)&tamano_direccion );//Escuchamos la 1° conexion del cliente.
									if(n<0){perror("Recepcion");exit(1);}
									/*if(!strcmp( "ERROR", strtok(buffer," ")))                                               //Chequeamos si el cliente nos devolvio error en el archivo o no.
									{printf("ERROR en el archivo requerido.\n");
									 remove(parseado);}                                                                                     //Hubo error. Imprimimos mensajey borramos el archivo abierto listo para recibir.
									else
									{*/
									printf("recibi tamano en buffer %s\n",buffer);
									size=atoi(buffer);                                                                      
									printf("Tamano recibido %d\n",size);
									int recibidos=0;
									
									while(recibidos!=size){
									
									memset(buffer,0,TAM);
										printf("Por mandar mensaje de inicializacion\n");
										n= sendto( descriptor_socketUDP, (void *)buffer, TAM, 0, (struct sockaddr *)&struct_UDP, tamano_direccion ); //Hacemos una primera conexion con el servidor con el buffer en blanco                 
										if ( n < 0 ) {perror( "Escritura en socket" );}	
										
									memset( transf, 0, 256 );                                       
									n=recvfrom( descriptor_socketUDP,(void *) transf, 256, 0, (struct sockaddr *)&struct_UDP,  (socklen_t*)&tamano_direccion);//Recibimos n bytes.
									if(n<0){perror("Recepcion");exit(1);}
									fwrite(transf,1,n,fp);
									printf("escribi!! %d recibidos %d size %d\n",n,recibidos,size);                                                         //Escribimos los n bytes en el archivo.
									recibidos=recibidos+n;                                                                          //Vamos acumulando los bytes recibidos para saber cuando cortar.
									}
							
							
									// Empieza la transferencia. Hasta que no termina no sale del while
									/*while(grabado < fsize)
										{
										memset(buffer,0,TAM);
										n=sendto( descriptor_socketUDP,(void *) buffer, 256, 0, (struct sockaddr *)&struct_UDP,  tamano_direccion );
										printf("grabado: %lu, size %lu\n",grabado,fsize);
										memset(transf,0,256);
										//m = recvfrom( socket_server, (void *)transf, 256, 0, (struct sockaddr *) &struct_servidor, &tamano_direccion );
										m = recvfrom( descriptor_socketUDP, (void *)transf, 256, 0, (struct sockaddr *) &struct_UDP, &tamano_direccion );
										if (m < 0) error("ERROR reading from socket");
										m = fwrite(transf,1,m,fp);
										printf("grabado: %d, size %lu\n",m,fsize);
										if (m < 0) error("ERROR writing in file");
										grabado +=m;
										printf("grabado: %d, size %lu\n",m,fsize);
										}*/
										
								grabado=0;
								fclose(fp);
								
								}
							} // FIN PUT
///////////////   ES GET       //////////////////////
							else
							{
	
							 
							 directorio= strstr(transf," ");							// En directorio dejo el path
							 directorio++;
							 directorio[strlen(directorio)-1] = '\0';
							printf("archivo: %s\n",directorio);
							
							
							if(stat(directorio, &sts)==0)
							{
								memset(buffer,0,TAM);strcpy(buffer,"Existe el archivo. Comenzando la transferencia.\n");
								n = write( newsockfd, buffer,strlen(buffer) );
								
								printf("antes de abrir! archivo: %s\n",directorio);
								fp = fopen(directorio,"rb"); 								
								fseek(fp,0,SEEK_END);
								fsize = ftell(fp);
								rewind(fp);
								printf("File contains %ld bytes!\n", fsize);
								
						memset(buffer,0,TAM);		
						n = recvfrom( descriptor_socketUDP, buffer, TAM, 0, (struct sockaddr *)&struct_UDP,  (socklen_t*)&tamano_direccion );
                        if(n<0){perror("ERROR en lectura de socket");}
                        printf("Recibi mensaje de inicializacion\n");
                                                                                                              
                        
						sprintf(buffer,"%lu",fsize);
                                                                                                
                        n = sendto( descriptor_socketUDP, buffer, TAM, 0, (struct sockaddr *)&struct_UDP,  tamano_direccion );
                        if(n<0){perror("Escritura");}
                        printf("Mande el tamano %s y SIZE %lu\n",buffer,fsize);
                                                
                        int enviados=0,leidos=0;
                        while(enviados!=fsize){
							
							memset(buffer,0,TAM);		
						n = recvfrom( descriptor_socketUDP, buffer, TAM, 0, (struct sockaddr *)&struct_UDP,  (socklen_t*)&tamano_direccion );
                        if(n<0){perror("ERROR en lectura de socket");}
                        printf("Recibi mensaje de inicializacion\n");
							
							
                        memset( transf, 0, 256 );                                       
                        leidos=fread(transf,1,256,fp);                                       
                        n=sendto( descriptor_socketUDP,(void *) transf, leidos, 0, (struct sockaddr *)&struct_UDP,  tamano_direccion );
                        if(n<0){perror("Escritura");}
                        printf("bytes read para escribir %d, bytes written %d, SIZE %lu\n",leidos,enviados,fsize);
                        enviados=enviados+leidos;                                                                      
                        }
								
								
							/*	 while(1)
									{	
										
										
										
										memset(transf,0,256);
										int bytes_read = fread(transf,1,sizeof(transf),fp);
											printf("bytes read para escribir %d\n",bytes_read);
										
										if(bytes_read == 0)
										{printf("Termino la transferencia\n");break;} // Terminó la transferencia
										
										void *p = transf;
										//printf("TRANSF: %s",transf);
										while(bytes_read > 0)
										{
											
											printf("Espero para recibir vacio\n");
										 m = recvfrom( descriptor_socketUDP, (void *)transf, 256, 0, (struct sockaddr *)&struct_UDP, &tamano_direccion );
										printf("Recibi vacio\n");
										//sendto( descriptor_socketUDP, transf, 256, 0, (struct sockaddr *)&struct_UDP, sizeof(struct_UDP) );
										 
												
									//	printf("por mandar, DESCRIP: %d,TRANSF: %s, BYTES_READ: %d, \n",descriptor_socket,transf,bytes_read);
										//int bytes_written = sendto( socket_server, transf, bytes_read, 0, (struct sockaddr *)&struct_servidor, sizeof(struct_servidor) );
										int bytes_written = sendto( descriptor_socketUDP, transf, bytes_read, 0, (struct sockaddr *)&struct_UDP, sizeof(struct_UDP) );
										if(bytes_written<0){perror("written");}
										printf("bytes read para escribir %d, bytes written %d,\n",bytes_read,bytes_written);
										bytes_read -= bytes_written;
										p += bytes_written;
										}
				 
									}*/
								
								fclose(fp);
								
								}
							
							
							else{memset(buffer,0,TAM);strcpy(buffer,"No existe el archivo.\n");}
							
							}	
						}  
					}
				else{
					buffer[strlen(buffer)-1] = '\0'; 						// Para analizar tanto el comando fin como cd solos agregamos '\0'
					if(!strcmp("fin",buffer))
						{printf( "Como recibí 'fin', termino la ejecución.\n\n");	// Si es fin imprimimos el mensaje y matamos el hijo
						 exit(0);
						}
					else if(!strcmp("cd",buffer))							// Si es cd solo, tenemos que ir a /home/usuario_logeado
						{memset( buffer,0, TAM );
						 strcpy(buffer,"/home/");
						 strcat(buffer,getlogin());							// Realizamos estas lineas para ir al directorio correcto
						 
						 int retu=chdir(buffer);
							if(retu==0){strcpy(buffer,ok);}					// Chequeamos si el path a sido correcto
							else{strcpy(buffer,not);}						// Si no, quiere decir que el chdir no se a podido completar con ÉXITO
						 }
					 
					
						else 
							{
							int nuevofd=dup(1);							// Si el comando ingresado no fue ni cd ni fin entonces es cualquier otro comando
							dup2(newsockfd,1);
								
							int r=system(buffer);
							dup2(nuevofd,1);
								if(r!=0){memset( buffer,0, TAM );strcpy(buffer,cmderr);}
								else{memset( buffer,0, TAM );}
							}
					     
					}
				

				printf("por escribir: \n");
				n = write( newsockfd, buffer,strlen(buffer) );
				if ( n < 0 ) 
					{
					perror( "escritura en socket" );
					exit( 1 );
					}
				
			}
		}
		else {
			printf( "SERVIDOR: Nuevo cliente, que atiende el proceso hijo: %d\n", pid );
			close( newsockfd );
		}
	}
	return 0; 
} 
